//: C12:Binary.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Overloading binary operators
#include "../require.h"
#include <fstream>
using namespace std;

ofstream out("binary.out");

class Integer   // Combine this with Unary.cpp
{
    long i;
public:
    Integer(long ll = 0) : i(ll) {}
    // Operators that create new, modified value:
    friend const Integer
    operator+(const Integer& left,
              const Integer& right);
    friend const Integer
    operator-(const Integer& left,
              const Integer& right);
    friend const Integer
    operator*(const Integer& left,
              const Integer& right);
    friend const Integer
    operator/(const Integer& left,
              const Integer& right);
    friend const Integer
    operator%(const Integer& left,
              const Integer& right);
    friend const Integer
    operator^(const Integer& left,
              const Integer& right);
    friend const Integer
    operator&(const Integer& left,
              const Integer& right);
    friend const Integer
    operator|(const Integer& left,
              const Integer& right);
    friend const Integer
    operator<<(const Integer& left,
               const Integer& right);
    friend const Integer
    operator>>(const Integer& left,
               const Integer& right);
    // Assignments modify & return lvalue:
    friend Integer&
    operator+=(Integer& left,
               const Integer& right);
    friend Integer&
    operator-=(Integer& left,
               const Integer& right);
    friend Integer&
    operator*=(Integer& left,
               const Integer& right);
    friend Integer&
    operator/=(Integer& left,
               const Integer& right);
    friend Integer&
    operator%=(Integer& left,
               const Integer& right);
    friend Integer&
    operator^=(Integer& left,
               const Integer& right);
    friend Integer&
    operator&=(Integer& left,
               const Integer& right);
    friend Integer&
    operator|=(Integer& left,
               const Integer& right);
    friend Integer&
    operator>>=(Integer& left,
                const Integer& right);
    friend Integer&
    operator<<=(Integer& left,
                const Integer& right);
    // Conditional operators return true/false:
    friend int
    operator==(const Integer& left,
               const Integer& right);
    friend int
    operator!=(const Integer& left,
               const Integer& right);
    friend int
    operator<(const Integer& left,
              const Integer& right);
    friend int
    operator>(const Integer& left,
              const Integer& right);
    friend int
    operator<=(const Integer& left,
               const Integer& right);
    friend int
    operator>=(const Integer& left,
               const Integer& right);
    friend int
    operator&&(const Integer& left,
               const Integer& right);
    friend int
    operator||(const Integer& left,
               const Integer& right);
    // Write the contents to an ostream:
    void print(ostream& os) const {
        os << i;
    }
};

const Integer
operator+(const Integer& left,
          const Integer& right)
{
    return Integer(left.i + right.i);
}
const Integer
operator-(const Integer& left,
          const Integer& right)
{
    return Integer(left.i - right.i);
}
const Integer
operator*(const Integer& left,
          const Integer& right)
{
    return Integer(left.i * right.i);
}
const Integer
operator/(const Integer& left,
          const Integer& right)
{
    require(right.i != 0, "divide by zero");
    return Integer(left.i / right.i);
}
const Integer
operator%(const Integer& left,
          const Integer& right)
{
    require(right.i != 0, "modulo by zero");
    return Integer(left.i % right.i);
}
const Integer
operator^(const Integer& left,
          const Integer& right)
{
    return Integer(left.i ^ right.i);
}
const Integer
operator&(const Integer& left,
          const Integer& right)
{
    return Integer(left.i & right.i);
}
const Integer
operator|(const Integer& left,
          const Integer& right)
{
    return Integer(left.i | right.i);
}
const Integer
operator<<(const Integer& left,
           const Integer& right)
{
    return Integer(left.i << right.i);
}
const Integer
operator>>(const Integer& left,
           const Integer& right)
{
    return Integer(left.i >> right.i);
}
// Assignments modify & return lvalue:
Integer& operator+=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i += right.i;
    return left;
}
Integer& operator-=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i -= right.i;
    return left;
}
Integer& operator*=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i *= right.i;
    return left;
}
Integer& operator/=(Integer& left,
                    const Integer& right)
{
    require(right.i != 0, "divide by zero");
    if (&left == &right) {/* self-assignment */}
    left.i /= right.i;
    return left;
}
Integer& operator%=(Integer& left,
                    const Integer& right)
{
    require(right.i != 0, "modulo by zero");
    if (&left == &right) {/* self-assignment */}
    left.i %= right.i;
    return left;
}
Integer& operator^=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i ^= right.i;
    return left;
}
Integer& operator&=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i &= right.i;
    return left;
}
Integer& operator|=(Integer& left,
                    const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i |= right.i;
    return left;
}
Integer& operator>>=(Integer& left,
                     const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i >>= right.i;
    return left;
}
Integer& operator<<=(Integer& left,
                     const Integer& right)
{
    if (&left == &right) {/* self-assignment */}
    left.i <<= right.i;
    return left;
}
// Conditional operators return true/false:
int operator==(const Integer& left,
               const Integer& right)
{
    return left.i == right.i;
}
int operator!=(const Integer& left,
               const Integer& right)
{
    return left.i != right.i;
}
int operator<(const Integer& left,
              const Integer& right)
{
    return left.i < right.i;
}
int operator>(const Integer& left,
              const Integer& right)
{
    return left.i > right.i;
}
int operator<=(const Integer& left,
               const Integer& right)
{
    return left.i <= right.i;
}
int operator>=(const Integer& left,
               const Integer& right)
{
    return left.i >= right.i;
}
int operator&&(const Integer& left,
               const Integer& right)
{
    return left.i && right.i;
}
int operator||(const Integer& left,
               const Integer& right)
{
    return left.i || right.i;
}

void h(Integer& c1, Integer& c2)
{
    // A complex expression:
    c1 += c1 * c2 + c2 % c1;
#define TRY(OP) \
  out << "c1 = "; c1.print(out); \
  out << ", c2 = "; c2.print(out); \
  out << ";  c1 " #OP " c2 produces "; \
  (c1 OP c2).print(out); \
  out << endl;
    TRY(+) TRY(-) TRY(*) TRY(/)
    TRY(%) TRY(^) TRY(&) TRY(|)
    TRY(<<) TRY(>>) TRY(+=) TRY(-=)
    TRY(*=) TRY(/=) TRY(%=) TRY(^=)
    TRY(&=) TRY(|=) TRY(>>=) TRY(<<=)
    // Conditionals:
#define TRYC(OP) \
  out << "c1 = "; c1.print(out); \
  out << ", c2 = "; c2.print(out); \
  out << ";  c1 " #OP " c2 produces "; \
  out << (c1 OP c2); \
  out << endl;
    TRYC(<) TRYC(>) TRYC(==) TRYC(!=) TRYC(<=)
    TRYC(>=) TRYC(&&) TRYC(||)
}

// Member operators (implicit "this"):
class Byte   // Combine this with Unary.cpp
{
    unsigned char b;
public:
    Byte(unsigned char bb = 0) : b(bb) {}
    // No side effects: const member function:
    const Byte
    operator+(const Byte& right) const {
        return Byte(b + right.b);
    }
    const Byte
    operator-(const Byte& right) const {
        return Byte(b - right.b);
    }
    const Byte
    operator*(const Byte& right) const {
        return Byte(b * right.b);
    }
    const Byte
    operator/(const Byte& right) const {
        require(right.b != 0, "divide by zero");
        return Byte(b / right.b);
    }
    const Byte
    operator%(const Byte& right) const {
        require(right.b != 0, "modulo by zero");
        return Byte(b % right.b);
    }
    const Byte
    operator^(const Byte& right) const {
        return Byte(b ^ right.b);
    }
    const Byte
    operator&(const Byte& right) const {
        return Byte(b & right.b);
    }
    const Byte
    operator|(const Byte& right) const {
        return Byte(b | right.b);
    }
    const Byte
    operator<<(const Byte& right) const {
        return Byte(b << right.b);
    }
    const Byte
    operator>>(const Byte& right) const {
        return Byte(b >> right.b);
    }
    // Assignments modify & return lvalue.
    // operator= can only be a member function:
    Byte& operator=(const Byte& right) {
        // Handle self-assignment:
        if (this == &right) return *this;
        b = right.b;
        return *this;
    }
    Byte& operator+=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b += right.b;
        return *this;
    }
    Byte& operator-=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b -= right.b;
        return *this;
    }
    Byte& operator*=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b *= right.b;
        return *this;
    }
    Byte& operator/=(const Byte& right) {
        require(right.b != 0, "divide by zero");
        if (this == &right) {/* self-assignment */}
        b /= right.b;
        return *this;
    }
    Byte& operator%=(const Byte& right) {
        require(right.b != 0, "modulo by zero");
        if (this == &right) {/* self-assignment */}
        b %= right.b;
        return *this;
    }
    Byte& operator^=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b ^= right.b;
        return *this;
    }
    Byte& operator&=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b &= right.b;
        return *this;
    }
    Byte& operator|=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b |= right.b;
        return *this;
    }
    Byte& operator>>=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b >>= right.b;
        return *this;
    }
    Byte& operator<<=(const Byte& right) {
        if (this == &right) {/* self-assignment */}
        b <<= right.b;
        return *this;
    }
    // Conditional operators return true/false:
    int operator==(const Byte& right) const {
        return b == right.b;
    }
    int operator!=(const Byte& right) const {
        return b != right.b;
    }
    int operator<(const Byte& right) const {
        return b < right.b;
    }
    int operator>(const Byte& right) const {
        return b > right.b;
    }
    int operator<=(const Byte& right) const {
        return b <= right.b;
    }
    int operator>=(const Byte& right) const {
        return b >= right.b;
    }
    int operator&&(const Byte& right) const {
        return b && right.b;
    }
    int operator||(const Byte& right) const {
        return b || right.b;
    }
    // Write the contents to an ostream:
    void print(ostream& os) const {
        os << "0x" << hex << int(b) << dec;
    }
};

void k(Byte& b1, Byte& b2)
{
    b1 = b1 * b2 + b2 % b1;

#define TRY2(OP) \
  out << "b1 = "; b1.print(out); \
  out << ", b2 = "; b2.print(out); \
  out << ";  b1 " #OP " b2 produces "; \
  (b1 OP b2).print(out); \
  out << endl;

    b1 = 9;
    b2 = 47;
    TRY2(+) TRY2(-) TRY2(*) TRY2(/)
    TRY2(%) TRY2(^) TRY2(&) TRY2(|)
    TRY2(<<) TRY2(>>) TRY2(+=) TRY2(-=)
    TRY2(*=) TRY2(/=) TRY2(%=) TRY2(^=)
    TRY2(&=) TRY2(|=) TRY2(>>=) TRY2(<<=)
    TRY2(=) // Assignment operator

    // Conditionals:
#define TRYC2(OP) \
  out << "b1 = "; b1.print(out); \
  out << ", b2 = "; b2.print(out); \
  out << ";  b1 " #OP " b2 produces "; \
  out << (b1 OP b2); \
  out << endl;

    b1 = 9;
    b2 = 47;
    TRYC2(<) TRYC2(>) TRYC2(==) TRYC2(!=) TRYC2(<=)
    TRYC2(>=) TRYC2(&&) TRYC2(||)

    // Chained assignment:
    Byte b3 = 92;
    b1 = b2 = b3;
}

int main()
{
    Integer c1(47), c2(9);
    h(c1, c2);
    out << "\n member functions:" << endl;
    Byte b1(47), b2(9);
    k(b1, b2);
} ///:~
